package com.gdust.hzx.manager.service.impl;

import com.gdust.hzx.manager.mapper.SysMenuMapper;
import com.gdust.hzx.manager.mapper.SysRoleMenuMapper;
import com.gdust.hzx.manager.service.SysMenuService;
import com.gdust.hzx.manager.utils.MenuHelper;
import com.gdust.spzx.common.exception.GdustException;
import com.gdust.hzx.model.entity.system.SysMenu;
import com.gdust.hzx.model.entity.system.SysUser;
import com.gdust.hzx.model.vo.common.ResultCodeEnum;
import com.gdust.hzx.model.vo.system.SysMenuVo;
import com.gdust.hzx.utils.AuthContextUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.LinkedList;
import java.util.List;

/**
 * ClassName: SysMenuServiceImpl
 * Package: com.gdust.spzx.manager.service.impl
 * Description:
 *
 * @Author syun
 * @Create 2024/11/24 15:44
 * @Version 1.0
 */
@Service
public class SysMenuServiceImpl implements SysMenuService {
    //注入角色表的mapper
    @Autowired
    private SysMenuMapper sysMenuMapper;

    //注入角色菜单关系表的mapper
    @Autowired
    private SysRoleMenuMapper sysRoleMenuMapper;

    /**
     * 查询菜单：获取所有菜单数据并以树形结构返回
     *
     * 功能描述：
     * 1. 从数据库中查询所有菜单数据，返回一个菜单列表。
     * 2. 利用工具类将菜单数据组织为树形结构，便于前端展示。
     *
     * 返回结果：
     * - 若数据库中没有菜单数据，返回 `null`。
     * - 若有菜单数据，则返回封装为树形结构的菜单列表。
     *
     * @return 包含树形结构的菜单列表
     */
    @Override
    public List<SysMenu> findNodes() {
        //1.查询所有菜单，返回list集合
        // 调用 Mapper 层方法获取菜单数据
        List<SysMenu> sysMenuList = sysMenuMapper.findAll();

        // 检查菜单列表是否为空
        if(CollectionUtils.isEmpty(sysMenuList)){
            return null;
        }

        // 2. 构建树形结构的菜单列表
        // 调用工具类 `MenuHelper` 的方法，将线性菜单数据封装为树形结构
        List<SysMenu> treeList = MenuHelper.buildTree(sysMenuList);//构建树形数据
        return treeList;// 返回构建后的树形菜单列表
    }

    /**
     * 添加菜单
     * 字段is_half:表示菜单节点是否是半选中状态
     * @param sysMenu 系统菜单实体类
     */
    @Override
    public void save(SysMenu sysMenu) {
        // 添加新的节点
        sysMenuMapper.save(sysMenu);

        //新添加子菜单，把父菜单isHalf半开状态 1
        updateSysRoleMenu(sysMenu);
    }

    /**
     * 新添加子菜单，把父菜单isHalf半开状态 1
     * @param sysMenu
     */
    private void updateSysRoleMenu(SysMenu sysMenu) {
        //获取当前添加菜单的父菜单---查询是否存在父节点
        Long parentId = sysMenu.getParentId();
        SysMenu parentMenu = sysMenuMapper.selectParentMenu(parentId);

        if(parentMenu != null){
            //把父菜单isHalf半开状态1
            Long parentMenuId = parentMenu.getId();
            sysRoleMenuMapper.updateSysRoleMenuIsHalf(parentMenuId);
            //递归调用
            updateSysRoleMenu(parentMenu) ;
        }


    }


    /**
     * 修改菜单
     * @param sysMenu 系统菜单实体类
     */
    @Override
    public void update(SysMenu sysMenu) {
        sysMenuMapper.update(sysMenu);
    }

    /**
     * 删除菜单
     *
     * 思路：查询当前菜单是否包含子菜单，如果没有子菜单则直接删除，如果包含子菜单则抛出自定义异常提示无法直接删除
     *
     * @param id 菜单的唯一标识（路径参数）
     */
    @Override
    public void removeById(Long id) {
        //1.根据当前菜单id，查询是否包含子菜单
        int count = sysMenuMapper.selectCountById(id);//自定义方法查询子菜单

        //2.判断，count大于0，则包含子菜单
        if(count > 0){
            //抛出自定义异常：该节点下有子节点，不可以删除
            throw new GdustException(ResultCodeEnum.NODE_ERROR);
        }

        //3.count等于0，直接删除
        sysMenuMapper.delete(id);//自定义方法删除

    }

    /**
     * 查询用户可以操作的菜单
     *
     * 设计思路：
     * 1. 不同用户拥有不同的权限，因此左侧菜单需要根据当前登录用户的角色动态生成。
     * 2. 查询逻辑基于用户的ID，先获取其角色所对应的权限，再构建菜单树形结构返回。
     *
     * @return 返回用户可操作菜单的树形结构（List<SysMenuVo>）
     */
    @Override
    public List<SysMenuVo> findMenusByUserId() {
        //从当前线程中获取当前用户
        SysUser sysUser = AuthContextUtil.get();
        //从用户中获取id
        Long userId = sysUser.getId();

        //根据用户id查询可操作菜单,封装要求数据格式返回
        // 调用 sysMenuMapper 的方法，根据用户 ID 获取菜单列表
        // 使用 MenuHelper 的静态方法将菜单列表构建为树形结构,递归实现树形菜单的构建
        // buildTree 方法会将平铺的菜单列表转换为层级结构
        List<SysMenu> sysMenuList =
                MenuHelper.buildTree(sysMenuMapper.findMenusByUserId(userId));

        //SysMenuVo类包含：系统菜单标题、系统菜单名称、系统菜单子菜单列表
        //调用buildMenus方法，将List<SysMenu>对象转换成List<SysMenuVo>对象
        List<SysMenuVo> sysMenuVos = this.buildMenus(sysMenuList);

        // 返回转换后的菜单数据
        return sysMenuVos;
    }

    /**
     * 将系统菜单的实体列表（List<SysMenu>）转换为视图层菜单对象列表（List<SysMenuVo>）
     *
     * 转换过程：
     * 1. 遍历菜单列表，将每个 SysMenu 对象的属性映射到 SysMenuVo 对象。
     * 2. 如果 SysMenu 对象包含子菜单，则递归调用本方法处理子菜单。
     *
     * @param menus 系统菜单的树形结构列表（List<SysMenu>）
     * @return 转换后的视图层菜单列表（List<SysMenuVo>）
     */
    private List<SysMenuVo> buildMenus(List<SysMenu> menus) {
        // 创建一个列表，用于存储转换后的 SysMenuVo 对象
        List<SysMenuVo> sysMenuVoList = new LinkedList<SysMenuVo>();

        // 遍历菜单列表
        for (SysMenu sysMenu : menus) {
            // 创建 SysMenuVo 对象，用于存储菜单的视图层数据
            SysMenuVo sysMenuVo = new SysMenuVo();
            // 设置菜单标题
            sysMenuVo.setTitle(sysMenu.getTitle());
            // 设置菜单组件名称
            sysMenuVo.setName(sysMenu.getComponent());

            // 处理子菜单
            // 设置菜单组件名称（即菜单对应的页面）
            List<SysMenu> children = sysMenu.getChildren();
            if (!CollectionUtils.isEmpty(children)) {
                // 递归调用本方法，将子菜单转换为 SysMenuVo 对象列表
                sysMenuVo.setChildren(buildMenus(children));
            }
            // 将当前 SysMenuVo 对象添加到结果列表
            sysMenuVoList.add(sysMenuVo);
        }
        // 返回转换后的 SysMenuVo 列表
        return sysMenuVoList;
    }

}
